package com.googlecode.cswish.struts.hibernate;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.persistence.EntityManagerFactory;
import javax.persistence.spi.PersistenceUnitInfo;

import org.apache.log4j.Logger;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.PersistentClass;
import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
import org.springframework.stereotype.Service;

import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.bean.ModelUtil;

/**
 * Find hibernate configuration
 * 	 
 * @author jerry.feng Date: 2008-10-11
 */

@Service
public class HibernateConfiguration {

	private static final Logger logger = Logger.getLogger(HibernateConfiguration.class);
	
	private volatile List<Configuration> configurations;
	
	@Resource
	private EntityManagerFactory entityManagerFactory;
	
	private SessionFactory sessionFactory;
	
	public boolean isJPA;
	
	@Resource
	private ObjectFactory objectFactory;

	@PostConstruct
	private Collection<Configuration> initConfiguration() {
		// Configuration config = HibernateJpaVendorAdapter.getConfiguration();
		if (configurations == null) {
			synchronized (this) {
				if (configurations == null) {
					configurations = new ArrayList<Configuration>();
					// try to load by JPA
					isJPA = objectFactory.containsBeanDefinition("entityManagerFactory");
					if (isJPA) {
						//sessionFactory = (SessionFactory) objectFactory.getBean("entityManagerFactory", false);
						JPASessionFactory jpaSessionFactory = objectFactory.getBean(JPASessionFactory.class, false);
						this.sessionFactory = jpaSessionFactory.getSessionFactory();
						
						Map<String, Configuration> cfgs = HibernateJpaVendorAdapter.getConfigurations();
						if (cfgs != null) {
							configurations.addAll(cfgs.values());
						} else {
							configurations.addAll(loadJPAConfiguration());
						}
					} else {
						// TODO: initialize sessionFactory
						
					}
				}
			}
		}
		return configurations;
	}
	
	public PersistentClass getClassMapping(String entityName) {
		for (Configuration cfg : initConfiguration()) {
			PersistentClass clazz = cfg.getClassMapping(entityName);
			if (clazz != null) {
				return clazz;
			}
		}
		return null;
	}
	
	public void updateClassMapping(String entityName, PersistentClass clazz) {
		PersistentClass originalClass = getClassMapping(entityName);
		ModelUtil.copy(clazz, originalClass);
	}
	
	public List<Configuration> getConfigurations() {
		return configurations;
	}

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	
	public EntityManagerFactory getEntityManagerFactory() {
		return this.entityManagerFactory;
	}

	private List<Configuration> loadJPAConfiguration() {
		logger.debug("Hibernate factory is JPA factory");
		
		List<Configuration> configurations = new ArrayList<Configuration>();
		String message = "Load hibernate resource again...\n" +
				"********** INFO STARTING UP SPRING-HIBERNATE INTEGRATION **********\n" +
		        "You might need to use the following in applicationContext.xml for better performance: \n" +
		        "<property name=\"jpaVendorAdapter\">\n" +
				"    <bean class=\"com.googlecode.cswish.struts.hibernate.HibernateJpaVendorAdapter\">\n" +
				"	     ...\n" +
				"    </bean>\n" +
				"</property>\n";
		logger.info(message);
		DefaultPersistenceUnitManager persistenceUnitManager =
			new DefaultPersistenceUnitManager();
		persistenceUnitManager.preparePersistenceUnitInfos();
		PersistenceUnitInfo persistenceUnitInfo = persistenceUnitManager.obtainDefaultPersistenceUnitInfo();
		
		new HibernateJpaVendorAdapter().createManagerFactory(persistenceUnitInfo, new HashMap());
		Configuration cfg = HibernateJpaVendorAdapter.getConfigurations().get(persistenceUnitInfo.getPersistenceUnitName());
		configurations.add(cfg);
		return configurations;
	}
}